重学数据结构008——AVL树

之前学习了二叉查找树的及相关操作。二叉查找树的大部分主要操作的复杂度都是O(logN)量级的。现在考虑这样一种情况:通过集合{3,2,4,1,0,-1,-2,-3,-4,-4}中的元素来构建二叉查找树,得到的树如图所示:

image

如果现在我们需要查找元素-4,那么时间复杂度还是是O(logN)吗?有个更加极端的例子,假设数据集是{6,5,3,1,0,-1,-2,-3,-4,-4}呢?再去查找元素-4,其复杂度已经是O(N)了。也就是说,二叉查找树的查找优势完全不复存在了。在这样的情况下,以前的牛人们又想出了别的办法:让二叉查找树除了满足现有条件外,添加平衡条件,形成平衡二叉树。AVL树是其中一种典型的平衡二叉树,

AVL树是带有平衡条件的二叉查找树。在满足所有二叉查找树条件下,还加入了平衡条件:AVl树的每个节点的左右子树的高度差必须<=1。 在我使用的这本教材中,定义空树的高度为-1,一个节点的树的高度定义为0。

为了满足平衡条件,AVL树的基本操作一般涉及运作同在不平衡的二叉查找树所运作的同样的算法。但是要进行预先或随后做一次或多次所谓的"AVL旋转"。

涉及到的几种情况如下图所示:

Tree_Rebalancing

AVL树的基本操作代码如下:

#include <stdio.h> #include <stdlib.h> #define MAX(X,Y) ((X > Y) ? X : Y) typedef int ElementType; typedef struct AvlNode *Position; typedef struct AvlNode *AvlTree; /************************************************************************/ /* Avl树的数据结构定义以及节本操作定义 */ /************************************************************************/ struct AvlNode { ElementType Element; int Height; AvlTree Left,Right; }; AvlTree MakeEmpty(AvlTree T); /*清空Avl树*/ Position Find(ElementType X, AvlTree T); /*在Avl树中查找指定的元素*/ Position FindMax(AvlTree T); /*在Avl树中查找最大元素*/ Position FindMin(AvlTree T); /*在Avl树中查找最小元素*/ AvlTree Insert(ElementType X, AvlTree T); /*在Avl树中插入指定元素*/ AvlTree Delete(ElementType X, AvlTree T); /*在Avl树中删除指定元素*/ ElementType Retrieve(Position P); /*获取指定节点的元素值*/ int Height(Position P); /*获得指定节点的高度值*/ Position SingleRotateWithLeft(Position P); /*单右旋转*/ Position SingleRotateWithRight(Position P); /*单左旋转*/ Position DoubleRotateWithLeft(Position P); /*双左右旋转*/ Position DoubleRotateWithRight(Position P); /*双右左旋转*/ int main(void) { return 0; } /************************************************************************/ /* 清空AVL树的操作与清空普通二叉树相同 */ /************************************************************************/ AvlTree MakeEmpty(AvlTree T) { if (!T) { MakeEmpty(T->Left); MakeEmpty(T->Right); free(T); } return NULL; } /************************************************************************/ /*在Avl树中查找指定的元素,与二叉查找树的操作相同 */ /************************************************************************/ Position Find(ElementType X, AvlTree T) { if (T == NULL) { return NULL; } else { if (X < T->Element) { return Find(X,T->Left); } else if(X > T->Element) { return Find(X, T->Right); } else { return T; } } } /************************************************************************/ /* 在Avl树中查找最大值(递归写法) */ /************************************************************************/ Position FindMax(AvlTree T) { if (T == NULL) { return NULL; } else { if (T->Right == NULL) { return NULL; } else { return FindMax(T->Right); } } } /************************************************************************/ /* 在Avl树中查找最小值(非递归写法) */ /************************************************************************/ Position FindMin(AvlTree T) { if (T == NULL) { return NULL; } else { while (T->Left != NULL) { T = T->Left; } return T; } } /************************************************************************/ /* 返回指定节点的高度信息 */ /************************************************************************/ int Height(Position P) { if (P == NULL) { return -1; } else { return P->Height; } } /************************************************************************/ /* 单旋转:右旋转 */ /* 使用条件:这个函数只适合当P有左子树时调用; */ /* 作用:在P和其左子树根节点之间执行一次单右旋转 */ /************************************************************************/ Position SingleRotateWithLeft(Position P) { Position LChild = P->Left; P->Left = LChild->Right; /*将P的左孩子设置成LChild的右孩子*/ LChild->Right = P; P->Height = MAX(Height(P->Left),Height(P->Right)) + 1;/*更新高度信息*/ LChild->Height = MAX(Height(LChild->Left),P->Height) + 1; return LChild; /*新的根节点*/ } /************************************************************************/ /* 单旋转:左旋转 */ /* 使用条件:这个函数只适合当P有右子树时调用; */ /* 作用:在P和其右子树根节点之间执行一次单左旋转 */ /************************************************************************/ Position SingleRotateWithRight(Position P) { Position RChild = P->Right; /*将P的右孩子设置成RChild的右孩子*/ P->Right = RChild->Left; RChild->Left = P; P->Height = MAX(Height(P->Left),Height(P->Right)) + 1;/*更新高度信息*/ RChild->Height = MAX(Height(RChild->Right),P->Height) + 1; return RChild; /*新的根节点*/ } /************************************************************************/ /* 双旋转:左右旋转 */ /* 使用条件:适合于当P有左孩子,而左孩子有右孩子 */ /* 作用:*/ /************************************************************************/ Position DoubleRotateWithLeft(Position P) { P->Left = SingleRotateWithRight(P->Left); /*先进行左旋转*/ return SingleRotateWithLeft(P); /*再进行又旋转*/ } /************************************************************************/ /* 双旋转:右左旋转 */ /* 使用条件:适合于当P有右孩子,而右孩子有左孩子 */ /* 作用:*/ /************************************************************************/ Position DoubleRotateWithRight(Position P) { P->Right = SingleRotateWithLeft(P->Right); /*先进行右旋转*/ return SingleRotateWithRight(P); /*再进行左旋转*/ } /************************************************************************/ /* AVL树插入操作 */ /************************************************************************/ AvlTree Insert(ElementType X, AvlTree T) { /*如果T是一棵空树,那么创建一个节点作为树的根节点*/ if (T == NULL) { T = malloc(sizeof(struct AvlNode)); if(T == NULL) { fprintf(stderr,"Out of space!"); } else { T->Element = X; T->Left = NULL; T->Right = NULL; T->Height = 0; } }else { if (X < T->Element) { T->Left = Insert(X,T->Left); /*判断是否打破了破了平衡条件*/ if (Height(T->Left) - Height(T->Right) == 2) { /*判断是四种情况中的哪一种情况*/ if (X < T->Left->Element) { T = SingleRotateWithLeft(T); } else if (X > T->Left->Element) { T = DoubleRotateWithLeft(T); } } } else if (X > T->Element) { T->Right = Insert(X,T->Right); if (Height(T->Right) - Height(T->Left) == 2) { if(X < T->Right->Element) { T = DoubleRotateWithRight(T); } else if (X > T->Right->Element) { T = SingleRotateWithRight(T); } } } else { /*元素已经存在于AVL树中,因此不需要再做别的工作*/ } /*更新数的高度信息*/ T->Height = MAX(Height(T->Left),Height(T->Right)) + 1; } return T; }


上述AVL旋转的示意图来自维基百科:AVL树

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 山东大学数据结构课程设计要求实现AVL树AVL树是一种自平衡二叉搜索树,具有良好的平衡性。在AVL树中,每个节点的左子树和右子树的高度差不超过1。这种平衡性使得AVL树的查找、插入和删除操作的时间复杂度都为O(logn),相较于其他平衡二叉搜索树如红黑树而言,AVL树的查找性能更好。 实现AVL树主要包括以下几个步骤: 1. 定义AVL树的节点结构,包含数据域和左右子节点指针。 2. 实现AVL树的插入操作。插入操作首先按照二叉搜索树的规则找到要插入的位置,然后进行平衡调整。插入操作的平衡调整包括修改各个节点的平衡因子,根据不同的情况进行树的旋转操作,以保证树的平衡性。 3. 实现AVL树的删除操作。删除操作首先按照二叉搜索树的规则找到要删除的节点,然后进行平衡调整。删除操作的平衡调整包括修改各个节点的平衡因子,并根据不同的情况进行树的旋转操作,以保证树的平衡性。 4. 实现AVL树的查找操作。查找操作按照二叉搜索树的规则进行,即根据节点的大小关系不断在左子树或右子树中查找,直到找到目标节点或者为空节点。 在实现AVL树的过程中,需要注意保持树的平衡性,并根据旋转操作的具体情况进行适当的调整。此外,还可以实现一些辅助函数,如计算节点的高度、更新节点的平衡因子等,以提高代码的可读性和维护性。 总之,山东大学数据结构课程设计要求实现AVL树,通过定义节点结构和实现插入、删除和查找操作,可以实现一个具有良好平衡性和高效性能的AVL树。 ### 回答2: 山东大学的数据结构课程设计中,我实现了AVL树AVL树是一种平衡二叉搜索树,它的目的是保持树的平衡,以避免在搜索、插入和删除操作中产生较高的时间复杂度。AVL树通过在每个节点上维护一个平衡因子(即左子树高度减去右子树高度),来确保树的平衡。 在我的实现中,首先我定义了一个AVLNode结构体,它包含了存储在树节点中的数据以及两个指向左右子节点的指针。然后,我实现了一些基本操作函数,包括实现了插入函数、删除函数、查找函数等等。 在插入函数中,我通过递归地将新节点插入到树中,并在插入完成后更新每个节点的平衡因子。这样,如果平衡因子超过了允许的范围(例如-1到1之外),我就需要进行相应的旋转操作来恢复树的平衡。 在删除函数中,我首先找到要删除的节点,并按照BST的规则进行删除操作。删除后,我还需要更新其父节点及其祖先节点的平衡因子,并通过旋转操作来保持树的平衡性。 除了插入和删除操作,我还实现了一些其他的功能,例如查找最小值、查找最大值、查找后继节点、查找前驱节点等。 在整个实现过程中,我注重了代码的可读性和效率。我使用了递归来处理树的节点,利用平衡因子来判断树的平衡性,采用适当的旋转操作来维持树的平衡。通过测试样例,我验证了实现的正确性和性能。 ### 回答3: AVL树是一种自平衡的二叉搜索树,它的设计旨在解决二叉搜索树在插入、删除等操作过程中可能导致不平衡的问题。 在山东大学数据结构课程设计中,我们可以采用以下步骤实现AVL树: 1. 首先,我们需要定义AVL树的节点结构,该结构包括左右孩子指针、平衡因子和关键字等信息。可以使用结构体来表示节点。 2. 接着,我们实现插入操作。当插入一个新的节点时,我们需要按照二叉搜索树的规则找到插入位置,并将节点插入到相应的位置。插入完成后,我们需要逐级向上更新每个节点的平衡因子,并检查是否需要进行旋转操作。 3. 为了保持树的平衡性,我们需要定义旋转操作。主要有四种旋转操作:左单旋、右单旋、左-右双旋和右-左双旋。这些旋转操作能够通过改变节点之间的链接关系,使树重新平衡。 4. 我们还需要实现删除操作。删除节点时,我们首先找到要删除的节点,并根据二叉搜索树的规则调整树的结构。删除完成后,同样需要逐级向上更新每个节点的平衡因子,并检查是否需要进行旋转操作。 5. 最后,我们需要实现一些辅助函数,如计算树的高度、查找最小值和最大值等。 在实现AVL树时,需要注意维护树的平衡性,并确保插入和删除操作的正确性。此外,可在实现过程中添加必要的错误处理、输入验证和合理的注释,以提高代码的稳定性和可读性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值